﻿using SignalBox.Core.Model;
using SignalBox.Core.Model.Graph;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace SignalBox.Core.Util
{
    public class RouteFinding
    {

        private static List<TrackCell> route;
        private static DIRECTION currentDirection;
        private static TrackLayout _trackLayout;
        private static LayoutGraph _graphLayout;
        private static GraphNode _currentNode;

        public static List<TrackCell> FindRouteFromSignal(TrackLayout trackLayout, TrackCell start)
        {



            route = new List<TrackCell>();
            _trackLayout = trackLayout;
            currentDirection = start.TrackCellType.GetSignalDirectionOutput(start.TrackCellType.TheTrackCellType);
            route.Add(start);

            _graphLayout = new LayoutGraph(trackLayout, false);
            _currentNode = _graphLayout.findNodeAt(start.X, start.Y);

            if (!FindRoute())
            {
                route.Clear();
            }

            return route;
        }

        public static Point GetNextCell(Point currentCell, DIRECTION dir)
        {

            Point nextCell = new Point(currentCell.X, currentCell.Y);


            switch (dir)
            {
                case DIRECTION.E:
                case DIRECTION.NE:
                case DIRECTION.SE:
                    nextCell.X++;
                    break;
                case DIRECTION.W:
                case DIRECTION.NW:
                case DIRECTION.SW:
                    nextCell.X--;
                    break;
            }

            switch (dir)
            {
                case DIRECTION.N:
                case DIRECTION.NE:
                case DIRECTION.NW:
                    nextCell.Y--;
                    break;
                case DIRECTION.S:
                case DIRECTION.SE:
                case DIRECTION.SW:
                    nextCell.Y++;
                    break;
            }

            return nextCell;
        }

        public static DIRECTION GetOppositeDirection(DIRECTION dir)
        {
            switch (dir)
            {
                case DIRECTION.E:
                    return DIRECTION.W;
                case DIRECTION.N:
                    return DIRECTION.S;
                case DIRECTION.NE:
                    return DIRECTION.SW;
                case DIRECTION.NW:
                    return DIRECTION.SE;
                case DIRECTION.S:
                    return DIRECTION.N;
                case DIRECTION.SE:
                    return DIRECTION.NW;
                case DIRECTION.SW:
                    return DIRECTION.NE;
                case DIRECTION.W:
                    return DIRECTION.E;
            }

            return DIRECTION.E;
        }

        private static bool FindRoute()
        {

            TrackCell current = route[route.Count - 1];

            _currentNode = _currentNode.GetNextNode(currentDirection);

            //Point nextPoint = GetNextCell(new Point(current.X, current.Y), currentDirection);

            //TrackCell next = _trackLayout.getCell(nextPoint.X, nextPoint.Y);
            TrackCell next = _currentNode.Cell;

            route.Add(next);


            if (next.TrackCellType.Category == "SIGNAL" && next.TrackCellType.GetSignalDirectionOutput(next.TrackCellType.TheTrackCellType) == currentDirection)
            {

                route.Remove(next);
                return true;

            }
            if (next.TrackCellType.TheTrackCellType == TRACKCELLTYPE.ENTRY_E ||
                next.TrackCellType.TheTrackCellType == TRACKCELLTYPE.ENTRY_N ||
                next.TrackCellType.TheTrackCellType == TRACKCELLTYPE.ENTRY_S ||
                next.TrackCellType.TheTrackCellType == TRACKCELLTYPE.ENTRY_W)
            {
                return true;
            }
            currentDirection = _currentNode.GetNextDirectionFromInput(GetOppositeDirection(currentDirection));

            if (currentDirection == DIRECTION.INVALID)
                return false;

            return FindRoute();

        }

        public static void UpdateSignals(TrackLayout trackLayout)
        {
            SignalGraph sigGraph = new SignalGraph(trackLayout);


            
            foreach (SignalNode sigNode in sigGraph.Signals)
            {

                if (sigNode.Cell.TrackCellType.Category == "ENTRYEXIT")
                {
                    bool prevState = sigNode.Cell.Locked;
                    sigNode.Cell.Locked = true;
                    CheckPreviousSignalsFromExit(sigNode);
                    sigNode.Cell.Locked = prevState;
                }

                
                // if signal red
                if (!sigNode.Cell.Locked)
                {
                    CheckPreviousSignalsFromRedSignal(sigNode);
                    sigNode.Cell.State = 1;
                }
                
            }



        }

        private static void CheckPreviousSignalsFromExit(SignalNode sigNode)
        {
            SignalNode currSig = sigNode;

            while (currSig.PreviousSignal != null)
            {
                SignalNode prev = currSig.PreviousSignal;

                if (prev.Cell.Locked)
                {
                    prev.Cell.State = 4;
                }

                else
                {
                    break;
                }
                currSig = prev;
            }

        }


        private static void CheckPreviousSignalsFromRedSignal(SignalNode sigNode)
        {

            int prevCount = 0;

            SignalNode currSig = sigNode;

            while (currSig.PreviousSignal != null)
            {

                SignalNode prev = currSig.PreviousSignal;
                if (prev.Occupied)
                {
                    prev.Cell.State = 1;
                    break;
                }
                else if (prev.Cell.Locked)
                {
                    if (prevCount == 0)
                    {
                        if (prev.Cell.SignalAspect == SIGNALASPECTTYPE.FOUR)
                        {
                            prev.Cell.State = 2;
                        }
                        else if (prev.Cell.SignalAspect == SIGNALASPECTTYPE.THREE)
                        {
                            prev.Cell.State = 3;
                        }
                        else if (prev.Cell.SignalAspect == SIGNALASPECTTYPE.TWO)
                        {
                            prev.Cell.State = 4;
                        }
                    }
                    else if (prevCount == 1)
                    {
                        if (prev.Cell.SignalAspect == SIGNALASPECTTYPE.FOUR)
                        {
                            prev.Cell.State = 3;
                        }
                        else if (prev.Cell.SignalAspect == SIGNALASPECTTYPE.THREE)
                        {
                            prev.Cell.State = 4;
                        }
                        else if (prev.Cell.SignalAspect == SIGNALASPECTTYPE.TWO)
                        {
                            prev.Cell.State = 4;
                        }
                    }
                    else if (prevCount > 1)
                    {
                        prev.Cell.State = 4;
                    }
                }
                else
                {
                    break;
                }

                currSig = prev;
                prevCount++;

            }

        }

        public static List<TrackCell> GetRouteToNextStop(TrackLayout trackLayout, TrackCell c, DIRECTION dir, TrainStop stop)
        {
            List<TrackCell> route = new List<TrackCell>();

            route.Add(c);

            LayoutGraph lg = new LayoutGraph(trackLayout, false);

            GraphNode node = lg.findNode(c);

            while ((node.Cell.Name == string.Empty || stop.Location.IndexOf(node.Cell.Name) < 0) && (node.Cell.TrackCellType.Category != "ENTRYEXIT" || (node.Cell.TrackCellType.Category == "ENTRYEXIT" && node.Cell.TrackCellType.GetOutputDirectionForEntry() == dir)))
            {
                dir = node.GetNextDirectionFromInput(GetOppositeDirection(dir));
                if (dir == DIRECTION.INVALID)
                {
                    route.Clear();
                    return route;
                }
                node = node.GetNextNode(dir);
                route.Add(node.Cell);
            }

            return route;
        }

        public static List<TrackCell> GetRouteToNextRedSignal(TrackLayout trackLayout, TrackCell c, DIRECTION dir)
        {

            List<TrackCell> route =new List<TrackCell>();

            route.Add(c);

            LayoutGraph lg = new LayoutGraph(trackLayout, false);

            GraphNode node = lg.findNode(c);

            while ((node.Cell.TrackCellType.Category != "SIGNAL" || (node.Cell.TrackCellType.Category == "SIGNAL" && node.Cell.Locked)))
            {
                if (node.Cell.TrackCellType.Category == "ENTRYEXIT" && node.GetNextNode(dir)==null)
                {
                    break;
                }
                dir = node.GetNextDirectionFromInput(GetOppositeDirection(dir));
                node = node.GetNextNode(dir);
                route.Add(node.Cell);
            }

            return route;
        }

        public static List<TrackCell> GetRouteToNextSpeedRestriction(TrackLayout trackLayout, TrackCell c, DIRECTION dir, out int maxSpeed)
        {

            List<TrackCell> route = new List<TrackCell>();

            route.Add(c);

            LayoutGraph lg = new LayoutGraph(trackLayout, false);

            GraphNode node = lg.findNode(c);

            

           
            while ((node.Cell.GetMaxSpeed(dir)==0) && (node.Cell.TrackCellType.Category != "ENTRYEXIT" || (node.Cell.TrackCellType.Category == "ENTRYEXIT" && node.Cell.TrackCellType.GetOutputDirectionForEntry()==dir)))
            {
                dir = node.GetNextDirectionFromInput(GetOppositeDirection(dir));
                node = node.GetNextNode(dir);
                route.Add(node.Cell);
            }

            maxSpeed = node.Cell.GetMaxSpeed(dir);

            return route;
        }

    }
}
